package cn.piggy.gateway.config.application;

import cn.piggy.gateway.config.config.GatewayServiceProperties;
import cn.piggy.gateway.config.domain.model.aggregates.ApplicationSystemRichInfo;
import cn.piggy.gateway.config.domain.model.vo.ApplicationInterfaceMethodVO;
import cn.piggy.gateway.config.domain.model.vo.ApplicationInterfaceVO;
import cn.piggy.gateway.config.domain.model.vo.ApplicationSystemVO;
import cn.piggy.gateway.config.domain.service.GatewayCenterService;
import cn.piggy.gateway.core.http.HttpCommandType;
import cn.piggy.gateway.core.http.HttpStatement;
import cn.piggy.gateway.core.config.ConfigHelper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import java.util.List;


public class GatewayApplication implements ApplicationContextAware, ApplicationListener<ContextClosedEvent> {

    private final Logger logger = LoggerFactory.getLogger(GatewayApplication.class);
    private GatewayServiceProperties properties;
    private GatewayCenterService gatewayCenterService;
    private ConfigHelper configHelper;
    private Channel gatewaySocketServerChannel;
    private ApplicationContext applicationContext;

    public GatewayApplication(GatewayServiceProperties properties, GatewayCenterService gatewayCenterService, ConfigHelper configHelper, Channel gatewaySocketServerChannel) {
        this.properties = properties;
        this.gatewayCenterService = gatewayCenterService;
        this.configHelper = configHelper;
        this.gatewaySocketServerChannel = gatewaySocketServerChannel;
    }

    public ApplicationContext getApplicationContext(){
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        try {
            // 1. 注册网关服务
            gatewayCenterService.doRegister(properties.getInterfaceAddress(), properties.getGroupId(),
                    properties.getGatewayId(), properties.getGatewayName(), properties.getGatewayAddress());
            // 2. 拉取接口信息
            addMappers();
        } catch (Exception e) {
            logger.error("网关服务启动失败，停止服务。{}", e.getMessage(), e);
            throw e;
        }
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        try {
            if (gatewaySocketServerChannel.isActive()) {
                logger.info("应用容器关闭，Api网关服务关闭。localAddress：{}", gatewaySocketServerChannel.localAddress());
                gatewaySocketServerChannel.close();
            }
        } catch (Exception e) {
            logger.error("应用容器关闭，Api网关服务关闭失败", e);
        }
    }

    public void receiveMessage(Object message) {
        logger.info("【事件通知】接收注册中心推送消息 message：{}", message);
        JSONObject jsonObject = JSON.parseObject(message.toString());
        String type = jsonObject.get("eventType").toString();
        String systemId = jsonObject.get("systemId").toString();
        switch (type){
            case "registerEvent":
                addMappers(jsonObject.get("systemId").toString());
                logger.info("--> {} --> {}",type, systemId);
                break;
            case "deleteEvent":
                deleteMappers(systemId);
                break;
            default:
                logger.info(type);
        }
    }

    public void addMappers(){
        addMappers("");
    }

    public void addMappers(String systemId) {
        // 2. 拉取网关配置；每个网关算力都会在注册中心分配上需要映射的RPC服务信息，包括；系统、接口、方法
        ApplicationSystemRichInfo applicationSystemRichInfo = gatewayCenterService.pullApplicationSystemRichInfo(properties.getInterfaceAddress(), properties.getGatewayId(), systemId);
        List<ApplicationSystemVO> applicationSystemVOList = applicationSystemRichInfo.getApplicationSystemVOList();
        if (applicationSystemVOList.isEmpty()) {
            logger.warn("网关{}服务注册映射为空，请排查 gatewayCenterService.pullApplicationSystemRichInfo 是否检索到此网关算力需要拉取的配置数据。", systemId);
            return;
        }
        for (ApplicationSystemVO system : applicationSystemVOList) {
            List<ApplicationInterfaceVO> interfaceList = system.getInterfaceList();
            // 接口
            for (ApplicationInterfaceVO itf : interfaceList) {
                // 2.1 创建配置信息加载注册
                configHelper.registryConfig(system.getSystemRegistry(), system.getSystemId(), itf.getInterfaceId(), itf.getInterfaceVersion());
                List<ApplicationInterfaceMethodVO> methodList = itf.getMethodList();
                // 2.2 注册系统服务接口信息
                for (ApplicationInterfaceMethodVO method : methodList) {
                    HttpStatement httpStatement = new HttpStatement(
                            system.getSystemId(), itf.getInterfaceId(),
                            method.getMethodId(),
                            method.getParameterType(),
                            method.getParameterName(),
                            method.getUri(),
                            HttpCommandType.valueOf(method.getHttpCommandType()),
                            method.isAuth(),method.getMethodType());
                    // todo 两种动态代理形式
//                    configuration.addMapperCjLib(httpStatement);
                    configHelper.addMapperJdk(httpStatement);
                    logger.info("网关服务注册映射 系统：{} 接口：{} 方法：{}", system.getSystemId(), itf.getInterfaceId(), method.getMethodId());
                }
            }
        }
    }

    public void deleteMappers(String systemId) {
        ApplicationSystemRichInfo applicationSystemRichInfo = gatewayCenterService.pullApplicationSystemRichInfo(properties.getInterfaceAddress(), properties.getGatewayId(), systemId);
        List<ApplicationSystemVO> applicationSystemVOList = applicationSystemRichInfo.getApplicationSystemVOList();
        for(ApplicationSystemVO applicationSystemVO : applicationSystemVOList){
            //registry application
            configHelper.deleteRegistryAndApplicationConfig(applicationSystemVO.getSystemId());
            List<ApplicationInterfaceVO> interfaceList = applicationSystemVO.getInterfaceList();
            for (ApplicationInterfaceVO applicationInterfaceVO : interfaceList){
                //reference
                configHelper.deleteReferenceConfig(applicationSystemVO.getSystemId(), applicationInterfaceVO.getInterfaceId());
                List<ApplicationInterfaceMethodVO> methodList = applicationInterfaceVO.getMethodList();
                for(ApplicationInterfaceMethodVO applicationInterfaceMethodVO : methodList){
                    String uri = applicationInterfaceMethodVO.getUri();
                    //httpStatementMap
                    configHelper.deleteMapperJdk(uri);
                }
            }
        }
    }
}